package com.gress.shop.annotation.aspect;

import com.alibaba.fastjson.JSON;
import com.gress.shop.annotation.FireLogger;
import com.gress.shop.bo.LogInfo;
import com.gress.shop.bo.Member;
import com.gress.shop.service.IAccessLogService;
import com.gress.shop.util.ShiroUtil;
import com.wordnik.swagger.annotations.Api;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Map;



@Component
@Aspect
public class FireLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(FireLogAspect.class);

    @Resource
    private IAccessLogService accessLogService;

    @After("@annotation(com.gress.shop.annotation.FireLogger)")
    @Async("asyncExecutor")
    public void after(JoinPoint joinPoint) {
        this.saveLog(joinPoint);
    }

    private void saveLog(JoinPoint joinPoint) {

        Member user = ShiroUtil.getUser();

        // 用户ID
        String userId = user.getEmail();
        if (StringUtils.isEmpty(userId)) {
            return;
        }
        // 用户名
        String userName = user.getName();
        if (StringUtils.isEmpty(userName)) {
            return;
        }

        // 模块
        String module = getClassApiAnnotation(joinPoint);
        if (StringUtils.isEmpty(module)) {
            return;
        }

        // 操作
        String action = getMethodLogAnnotation(joinPoint);
        if (StringUtils.isEmpty(action)) {
            return;
        }
        // 获取地址连接
        String url = getUrl(joinPoint);
        if (StringUtils.isEmpty(action)) {
            return;
        }
        // 参数
        String params = "";
        try {
            params = makeArgsString(joinPoint);
            logger.info(params);
        } catch (Exception e) {

        }
        LogInfo info = new LogInfo();
        info.setCreateBy(userId);
        info.setModule(module);
        info.setOpType(action);
        info.setOccurrenceUrl(url);
        info.setParams(params);
        info.setBackContent("站不考虑");
        try {
            accessLogService.saveLog(info);
        } catch (Exception e) {
            logger.error(e.getMessage());
        }

    }

    private String makeArgsString(JoinPoint joinPoint) {
        String result = "没有入参";
        Object[] args = joinPoint.getArgs();
        if (args != null && args.length > 0) {
            StringBuilder sb = new StringBuilder();
            for (Object param : args) {
                if (checkParamType(param)) {
                    continue;
                }
                sb.append(",");
                if (param instanceof MultipartFile) {
                    sb.append("\"");
                    sb.append(((MultipartFile) param).getOriginalFilename());
                    sb.append("\"");
                } else if (param instanceof Serializable) {
                    sb.append(JSON.toJSONString(param));
                } else {
                    sb.append(param);
                }
            }
            result = sb.substring(1);
        }

        return result;
    }

    /**
     * @param param
     * @Description: 过滤掉不需要的入参类型 ,因为这是个web 项目,会有很多入参需要过滤
     **/
    private boolean checkParamType(Object param) {
        //Model对象注入类型
        if (param instanceof Model || param instanceof ModelAndView || param instanceof ModelMap) {
            return true;
        }
        //文件类型过滤
        if (param instanceof File) {
            return true;
        }
        // 流形式入参
        if (param instanceof InputStream || param instanceof OutputStream) {
            return true;
        }
        // request response session
        if (param instanceof ServletRequest || param instanceof ServletResponse || param instanceof HttpSession) {
            return true;
        }
        return false;
    }

    private String getMethodLogAnnotation(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        if (signature instanceof MethodSignature) {
            Method method = ((MethodSignature) signature).getMethod();
            FireLogger fireLogger = method.getAnnotation(FireLogger.class);
            if (fireLogger != null) {
                return fireLogger.value();
            }
        }
        return null;
    }

    private String getUrl(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        if (signature instanceof MethodSignature) {
            Method method = ((MethodSignature) signature).getMethod();
            RequestMapping fireLogger = method.getAnnotation(RequestMapping.class);
            if (fireLogger != null) {
                String[] value = fireLogger.value();
                StringBuilder sb = new StringBuilder();
                if (value.length == 0) {
                    return sb.toString();
                } else if (value.length == 1) {
                    return value[0];
                } else {
                    for (int i= 0;i<value.length;i++) {
                        sb.append(value[i]);
                        if (i != value.length - 1) {
                            sb.append(",");
                        }
                    }
                }
            }
        }
        return null;
    }

    private String getClassApiAnnotation(JoinPoint joinPoint) {
        Class<?> clazz = joinPoint.getTarget().getClass();
        Api api = clazz.getAnnotation(Api.class);
        if (api != null) {
            return api.value();
        } else {
            return null;
        }
    }





}
